#include "ptwrap.hpp"
#include <deque>
#include <stdio.h>
#include <assert.h>

struct Message 
{
	virtual ~Message() { } // allow for polymorphism

	enum {
		EXIT,
		NORMAL // first normal type
	};
	int msgtype_;
	// ... whatever else is needed by the application ...
};


// [[ex04cc]]
// DEBUG descr A more efficient version of the queue that stops on an EXIT message from any writer.
class Queue 
{
	// DEBUG {
public:
	enum {
		DEFAULT_LIMIT = 1000
	};

	Queue(int limit = DEFAULT_LIMIT) :
		limit_(limit), 
		dqwr_(&deque1_), dqrd_(&deque2_), condrd_(condwr_)
	{ }

	virtual ~Queue()
	{ 
		Message *m;
		while (!deque1_.empty()) {
			m = deque1_.front();
			deque1_.pop_front();
			delete m;
		}
		while (!deque2_.empty()) {
			m = deque2_.front();
			deque2_.pop_front();
			delete m;
		}
	}

	// this combines the STL front() and pop_front()
	// since the popping must be atomic; the receiver
	// takes over the ownership of the message
	Message *pop_front()
	{
		pw::lockmutex lm(mutexrd_);

		while(dqrd_->empty()) {
			pw::lockmutex lm(condwr_);

			if (dqwr_->empty()) {
				condrd_.wait(); // it releases and gets back the mutex
			} else {
				swap(dqwr_, dqrd_);
				// if the write buffer was full, wake up the writers
				condwr_.broadcast();
			}
		}

		Message *m = dqrd_->front();
		dqrd_->pop_front();
		return m;
	}

	// takes over the ownership of the message;
	// returns true if the message was posted, false
	// if discarded
	virtual bool post(Message *m)
	{
		push_back(m); // the default implementation
		return true;
	}

protected:
	int limit_;
	std::deque<Message *> deque1_, deque2_;
	pw::pmutex mutexrd_;
	std::deque<Message *> *dqwr_, *dqrd_;
	pw::pmcond condwr_;
	pw::pchaincond condrd_;
	// DEBUG }
	// ...
protected:
	// Caller must have the mutex in condwr_ locked.
	void push_back_l(Message *m)
	{
		while (dqwr_->size() >= limit_)
			// it releases and gets back the mutex
			condwr_.wait(); 

		dqwr_->push_back(m);
		condrd_.broadcast();
	}
	void push_back(Message *m)
	{
		pw::lockmutex lm(condwr_);
		push_back_l(m);
	}
	// ...
};
class AnyExitQueue : public Queue
{
	// DEBUG {
public:
	AnyExitQueue(int limit = DEFAULT_LIMIT) : Queue (limit),
		exitposted_(false)
	{ }

protected:
	bool exitposted_;
	// DEBUG }
	// ...
public:
	virtual bool post(Message *m)
	{
		pw::lockmutex lm(condwr_);
		if (exitposted_) {
			delete m;
			return false;
		}
		if (m->msgtype_ == Message::EXIT)
			exitposted_ = true;
		push_back_l(m);
		return true;
	}
	// ...
	// no mutexx_ any more
};
// END

// DEBUG {
struct data : public Message
{
	int v_;

	data(int v, int kind = NORMAL) : v_(v)
	{ 
		Message::msgtype_ = kind;
	}
	~data()
	{
		fprintf(stderr, "deleted data %d\n", v_);
	}
};

int main()
{
	AnyExitQueue q;
	q.post(new data(1));
	q.post(new data(2));
	data * v2 = static_cast<data *>(q.pop_front());
	assert(v2->v_ == 1);
	delete v2;
	return 0;
}
/* Sample output:
deleted data 1
deleted data 2
*/
// DEBUG }
